<?php

namespace App\Services\Withdraw;

use App\Constant\Enum;
use App\Constant\User\UserMode;
use App\Exceptions\WithdrawException;
use App\Models\ReturnMoneyLog;
use App\Models\Setting;
use App\Models\Transaction;
use App\Models\User;
use App\Models\UserWallet;
use App\Models\UserWalletActive;
use App\Models\UserWithdraw;
use App\Services\Commission\CommissionService;
use App\Services\Third\TransactionService;
use App\Services\Wallet\UserWalletService;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class UserWithdrawService
{
    public static function apply(User $user, int $wid = 0)
    {
        if (($user->mode & UserMode::DISABLE_WITHDRAW) === UserMode::DISABLE_WITHDRAW) {
            throw new WithdrawException("提现暂不可用，请联系客服");
        }

        $withdrawConfig = Collection::make(Setting::get('withdrawals'))->where('id', $wid)->first() ?? [];
        $exchangeRate = Setting::get('exchange_rate') ?? '';
        $withdrawPerDay = Setting::get('withdraw_count_per_day') ?? 0;

        if ($withdrawPerDay > 0) {
            $wkey = sprintf('user_%s_withdraw_day', $user->getAttribute('id'));
            $count = Cache::get($wkey) ?? 0;
            if ($count >= $withdrawPerDay) {
                throw new WithdrawException("单日提现已达到限制，每日{$withdrawPerDay}次");
            }
        }

        if ($user->getAttribute('status') != Enum::STATUS_ENABLED) {
            throw new WithdrawException("用户状态异常，请联系客服");
        }


        if (empty($withdrawConfig)) {
            throw new WithdrawException('提现参数不正确');
        }
        if (empty($exchangeRate)) {
            throw new WithdrawException('兑换参数错误');
        }
        $wallet = $user->wallet;
        if (empty($wallet)) {
            throw new WithdrawException('用户帐号异常');
        }

        if ($wallet->on_withdraw) {
            throw new WithdrawException('已有提现处理中');
        }

        $limitPerDay = (int)$withdrawConfig['limit'];
        if (!empty($limitPerDay)) {
            $cachedKey = sprintf(
                'user_%s_withdraw_%s',
                $user->getAttribute('id'),
                sprintf('%.2f', $withdrawConfig['value'])
            );
            $put = Cache::get($cachedKey) ?? 0;
            if ($put >= $limitPerDay) {
                throw new WithdrawException("提现{$withdrawConfig['value']}元，每日{$limitPerDay}次");
            }
        }

        // 兑换需要扣除的金币数量
        $amount = $exchangeRate * $withdrawConfig['value'];
        $totalCoins = ($wallet->reward_coins + $wallet->commission_coins) - $wallet->locked_coins;
        if (($totalCoins - $amount) < 0) {
            throw new WithdrawException('账户余额不足');
        }

        DB::beginTransaction();
        try {
            UserWithdrawService::checkAccountBank();

            // 单位元
            $money = $amount / $exchangeRate;
            if ($wallet->reward_coins < $amount) {
                $walletType = Enum::WALLET_TYPE_BOTH;
            } else {
                $walletType = Enum::WALLET_TYPE_REWARD;
            }
            $withdraw = [
                'user_id' => $user->getAttribute('id'),
                'wid' => $wid,
                'wallet_type' => $walletType,
                'money' => $money,
                'exchange_rate' => $exchangeRate,
                'remark' => '',
                'amount' => $amount,
                'applied_at' => Carbon::now(),
            ];
            $withdrawLimit = Setting::get('withdraw_limit');
            $cachedKey = $wkey = null;
            if ($money > $withdrawLimit) {
                $withdraw['status'] = Enum::WITHDRAW_STATUS_PENDING;
            } else {
                $cachedKey = sprintf(
                    'user_%s_withdraw_%s',
                    $withdraw['user_id'],
                    sprintf("%.2f", $money)
                );
                $put = Cache::get($cachedKey) ?? 0;
                $expiresAt = Carbon::now()->endOfDay();
                $put += 1;

                $wkey = sprintf('user_%s_withdraw_day', $user->getAttribute('id'));
                $count = Cache::get($wkey) ?? 0;
                $count += 1;

                $withdraw['status'] = Enum::WITHDRAW_STATUS_APPROVED;
                $withdraw['remark'] = sprintf('自动通过(<%s元)', $withdrawLimit);
                $withdraw['audited_at'] = Carbon::now();
            }

            /** @var UserWithdraw $withdrawModel */
            $withdrawModel = UserWithdraw::query()->create($withdraw);

            if ($withdraw['status'] == Enum::WITHDRAW_STATUS_APPROVED) {
                TransactionService::transaction($withdrawModel);
            }
            //账变记录
            UserWalletService::outcome($withdrawModel, $amount);
            DB::commit();
            /*$transaction = Transaction::query()->where('user_withdraw_id',$withdrawModel->id)->first();

            $withdrawModel->transaction = $transaction;*/
            if ($wkey && $cachedKey) {
                Cache::put($wkey, $count, $expiresAt);
                Cache::put($cachedKey, $put, $expiresAt);
            }
            return $withdrawModel;
        } catch (\Exception $exception) {
            Log::error($exception);
            DB::rollBack();
            throw $exception;
        }
    }


    public static function approve(UserWithdraw $userWithdraw, string $remark = '', int $adminId = 0)
    {
        if ($userWithdraw->status !== Enum::WITHDRAW_STATUS_PENDING) {
            throw new \Exception('提现申请已处理');
        }

        DB::beginTransaction();
        try {
            // 提现审核状态修改
            $userWithdraw->update([
                'status' => Enum::WITHDRAW_STATUS_APPROVED,
                'remark' => $remark,
                'audited_at' => Carbon::now(),
                'operator_id' => $adminId,
            ]);
            TransactionService::transaction($userWithdraw);

            if ($userWithdraw->wallet_type != "return") {

                UserWithdrawService::checkAccountBank();

            }
            $cachedKey = sprintf(
                'user_%s_withdraw_%s',
                $userWithdraw->getAttribute('user_id'),
                sprintf("%.2f", $userWithdraw->getAttribute('money'))
            );
            $put = Cache::get($cachedKey) ?? 0;
            $expiresAt = Carbon::now()->endOfDay();
            $put += 1;

            $wkey = sprintf('user_%s_withdraw_day', $userWithdraw->getAttribute('user_id'));
            $count = Cache::get($wkey) ?? 0;
            $count += 1;

            DB::commit();

            Cache::put($cachedKey, $put, $expiresAt);
            Cache::put($wkey, $count, $expiresAt);
        } catch (\Exception $exception) {
            DB::rollBack();
            throw  $exception;
        }
    }

    public static function reject(UserWithdraw $userWithdraw, string $remark = '', int $adminId = 0)
    {
        /*if ($userWithdraw->status !== Enum::WITHDRAW_STATUS_PENDING) {
            throw new \Exception('提现申请已处理');
        }*/

        DB::beginTransaction();

        try {
            /** @var User $user */
            $user = $userWithdraw->user;

            if ($userWithdraw->wallet_type == "return") {

                ReturnMoneyLog::where("uid", $user->id)->where('status', 3)->update(['status' => 1]);
            } else {

                /** @var UserWallet $wallet */
                $wallet = $user->wallet;

                $wallet->update([
                    'user_withdraw_id' => 0,
                    'on_withdraw' => 0,
                    'locked_coins' => 0,
                ]);
                UserWalletActive::query()
                    ->where([
                        'user_id' => $user->getAttribute('id'),
                        'user_withdraw_id' => $userWithdraw->getAttribute('id')
                    ])
                    ->update([
                        'status' => Enum::WALLET_ACTIVE_STATUS_REJECT,
                    ]);
                Transaction::query()
                    ->where([
                        'user_id' => $user->getAttribute('id'),
                        'user_withdraw_id' => $userWithdraw->getAttribute('id')
                    ])
                    ->update([
                        'status' => Enum::WALLET_ACTIVE_STATUS_REJECT,
                    ]);
            }

            $userWithdraw->update([
                'status' => Enum::WITHDRAW_STATUS_REJECTED,
                'remark' => $remark,
                'audited_at' => Carbon::now(),
                'operator_id' => $adminId,
            ]);
            DB::commit();
        } catch (\Exception $exception) {
            DB::rollBack();
            throw  $exception;
        }
    }

    // 当审核通过处理用户账户
    public static function handleWalletWhenTransactionSuccess(UserWithdraw $userWithdraw)
    {
        /** @var User $user */
        $user = $userWithdraw->user;

        /** @var UserWallet $wallet */
        $wallet = $user->wallet;

        try {
            if ($userWithdraw->wallet_type == "return") {

                ReturnMoneyLog::where('uid', $user->id)->where('status', 3)->update(['status' => 4]);
            } else {
                //提现中的金币
                $amount = $wallet->locked_coins;
                if (($wallet->commission_coins + $wallet->reward_coins) - $amount < 0) {
                    throw new \Exception('用户账户金币不足');
                }
                $beforeCommissionCoins = $wallet->commission_coins;
                // 优先扣除收益
                $d1 = (int)($wallet->reward_coins - $amount);
                if ($d1 < 0) {
                    $d2 = $wallet->commission_coins + $d1;
                    $afterRewardCoins = 0;
                    $afterCommissionCoins = max($d2, 0);
                } else {
                    $afterRewardCoins = $d1;
                    $afterCommissionCoins = $beforeCommissionCoins;
                }

                $wallet->update([
                    'user_withdraw_id' => 0,
                    'on_withdraw' => 0,
                    'reward_coins' => $afterRewardCoins,
                    'commission_coins' => $afterCommissionCoins,
                    'locked_coins' => 0,
                ]);

                UserWalletActive::query()->where([
                    'user_id' => $user->getAttribute('id'),
                    'user_withdraw_id' => $userWithdraw->getAttribute('id'),
                ])->update([
                    'status' => Enum::WALLET_ACTIVE_STATUS_APPROVED,
                ]);

                // 奖励分发
                CommissionService::distribute($userWithdraw);

                $bank = Setting::get('bank');
                Setting::set('bank', [
                    'balance' => $bank['balance'] - $userWithdraw->getAttribute('money'),
                ]);
            }

        } catch (\Exception $exception) {
            Log::error($exception->getMessage());
            throw $exception;
        }
    }


    public static function checkAccountBank($money = 0)
    {
        $bank = Setting::get('bank');
        if (($bank['balance'] - $money) < $bank['lowest_balance']) {
            throw new WithdrawException(sprintf('[%s]提现账户余额不足', $bank['account_id']));
        }

        $pendingAmount = UserWithdraw::query()->where(['status' => Enum::WITHDRAW_STATUS_PENDING])->sum('money');
        if ($bank['balance'] - ($pendingAmount + $money) < 0) {
            throw new WithdrawException(sprintf('[%s]提现账户余额不足', $bank['account_id']));
        }
    }


    // 当审核通过但转账失败
    public static function handleWalletWhenTransactionFailed(UserWithdraw $userWithdraw)
    {
        DB::beginTransaction();
        try {
            /** @var User $user */
            $user = $userWithdraw->user;
            if ($userWithdraw->wallet_type == "return") {

                ReturnMoneyLog::where('uid', $user->id)->where('status', 3)->update(['status' => 1]);
            } else {

                /** @var UserWallet $wallet */
                $wallet = $user->wallet;

                $wallet->update([
                    'user_withdraw_id' => 0,
                    'on_withdraw' => 0,
                    'locked_coins' => 0,
                ]);
                UserWalletActive::query()
                    ->where([
                        'user_id' => $user->getAttribute('id'),
                        'user_withdraw_id' => $userWithdraw->getAttribute('id')
                    ])
                    ->update([
                        'status' => Enum::WALLET_ACTIVE_STATUS_REJECT,
                    ]);
            }

            DB::commit();
        } catch (\Exception $exception) {
            DB::rollBack();
            throw  $exception;
        }
    }
}
